package visitor;

import syntaxtree.*;

import java.io.*;
import java.util.*;

public class BDDTuppleGenerator extends GJDepthFirst<String, String> {
	public Vector<String> vMap;
	public Vector<String> hMap;
	public Vector<String> tMap;
	public Vector<String> mMap;
	public Vector<String> nMap;
	public Vector<String> iMap;
	// public Hashtable<String, Integer> hMapHash;
	// public Hashtable<String, Integer> iMapHash;
	public Vector<String> vP0Tupples;
	public Vector<String> assignTupples;
	public Vector<String> vTTupples;
	public Vector<String> hTTupples;
	public Vector<String> aTTupples;
	public Vector<String> chaTupples;
	public Vector<String> mITupples;
	public Vector<String> actualTupples;
	public Vector<String> formalTupples;

	public BDDTuppleGenerator(Vector<String> vMap, Vector<String> hMap,
			Vector<String> tMap, Vector<String> mMap, Vector<String> nMap,
			Vector<String> iMap) {
		this.vMap = vMap;
		this.hMap = hMap;
		this.tMap = tMap;
		this.mMap = mMap;
		this.nMap = nMap;
		this.iMap = iMap;
		// this.hMapHash = new Hashtable<String, Integer>();
		// this.iMapHash = new Hashtable<String, Integer>();
		vP0Tupples = new Vector<String>();
		assignTupples = new Vector<String>();
		vTTupples = new Vector<String>();
		hTTupples = new Vector<String>();
		aTTupples = new Vector<String>();
		chaTupples = new Vector<String>();
		mITupples = new Vector<String>();
		actualTupples = new Vector<String>();
		formalTupples = new Vector<String>();
	}

	public String visit(Goal g, String scope) {
		g.mainClass.accept(this, scope);
		for (Enumeration<Node> nodes = g.nodeListOptional.elements(); nodes
				.hasMoreElements();) {
			Node node = nodes.nextElement();
			node.accept(this, scope);
		}

		String heapitem = null;
		for (int i = 0; i < hMap.size(); i++) {
			heapitem = hMap.get(i);
			heapitem = heapitem.substring(heapitem.lastIndexOf('_') + 1);
			hTTupples.add(i + " " + tMap.indexOf(heapitem));
		}

		String s, t;
		int idx;

		for (int i = 0; i < mMap.size(); i++) {
			t = mMap.get(i);
			idx = t.indexOf('.');
			s = t.substring(idx + 1);
			t = t.substring(0, idx);
			if (tMap.indexOf(t) >= 0 && nMap.indexOf(s) >= 0)
				chaTupples.add(tMap.indexOf(t) + " " + nMap.indexOf(s) + " "
						+ i);
		}

		try {
			FileWriter vP0 = new FileWriter("vP0.tuples");
			BufferedWriter out = new BufferedWriter(vP0);
			String firstline = null;

			firstline = "# V0:"
					+ (int) Math.ceil(Math.log(vMap.size()) / Math.log(2))
					+ " H0:"
					+ (int) Math.ceil(Math.log(hMap.size()) / Math.log(2))
					+ "\n";
			// System.out.println(firstline);
			out.write(firstline);
			for (int i = 0; i < vP0Tupples.size(); i++)
				out.write(vP0Tupples.get(i) + "\n");
			out.close();

			FileWriter assign = new FileWriter("assign.tuples");
			out = new BufferedWriter(assign);
			firstline = "# V0:"
					+ (int) Math.ceil(Math.log(vMap.size()) / Math.log(2))
					+ " V1:"
					+ (int) Math.ceil(Math.log(vMap.size()) / Math.log(2))
					+ "\n";
			out.write(firstline);
			for (int i = 0; i < assignTupples.size(); i++)
				out.write(assignTupples.get(i) + "\n");
			out.close();

			FileWriter vT = new FileWriter("vT.tuples");
			out = new BufferedWriter(vT);
			firstline = "# V0:"
					+ (int) Math.ceil(Math.log(vMap.size()) / Math.log(2))
					+ " T0:"
					+ (int) Math.ceil(Math.log(tMap.size()) / Math.log(2))
					+ "\n";
			out.write(firstline);
			for (int i = 0; i < vTTupples.size(); i++)
				out.write(vTTupples.get(i) + "\n");
			out.close();

			FileWriter hT = new FileWriter("hT.tuples");
			out = new BufferedWriter(hT);
			firstline = "# H0:"
					+ (int) Math.ceil(Math.log(hMap.size()) / Math.log(2))
					+ " T0:"
					+ (int) Math.ceil(Math.log(tMap.size()) / Math.log(2))
					+ "\n";
			out.write(firstline);
			for (int i = 0; i < hTTupples.size(); i++)
				out.write(hTTupples.get(i) + "\n");
			out.close();

			FileWriter aT = new FileWriter("aT.tuples");
			out = new BufferedWriter(aT);
			firstline = "# T0:"
					+ (int) Math.ceil(Math.log(tMap.size()) / Math.log(2))
					+ " T1:"
					+ (int) Math.ceil(Math.log(tMap.size()) / Math.log(2))
					+ "\n";
			out.write(firstline);
			for (int i = 0; i < aTTupples.size(); i++)
				out.write(aTTupples.get(i) + "\n");
			out.close();

			FileWriter cha = new FileWriter("cha.tuples");
			out = new BufferedWriter(cha);
			firstline = "# T0:"
					+ (int) Math.ceil(Math.log(tMap.size()) / Math.log(2))
					+ " N0:"
					+ (int) Math.ceil(Math.log(nMap.size()) / Math.log(2))
					+ " M0:"
					+ (int) Math.ceil(Math.log(mMap.size()) / Math.log(2))
					+ "\n";
			out.write(firstline);
			for (int i = 0; i < chaTupples.size(); i++)
				out.write(chaTupples.get(i) + "\n");
			out.close();

			FileWriter mI = new FileWriter("mI.tuples");
			out = new BufferedWriter(mI);
			firstline = "# M0:"
					+ (int) Math.ceil(Math.log(mMap.size()) / Math.log(2))
					+ " I0:"
					+ (int) Math.ceil(Math.log(iMap.size()) / Math.log(2))
					+ " N0:"
					+ (int) Math.ceil(Math.log(nMap.size()) / Math.log(2))
					+ "\n";
			out.write(firstline);
			for (int i = 0; i < mITupples.size(); i++)
				out.write(mITupples.get(i) + "\n");
			out.close();

			FileWriter actual = new FileWriter("actual.tuples");
			out = new BufferedWriter(actual);
			firstline = "# I0:"
					+ (int) Math.ceil(Math.log(iMap.size()) / Math.log(2))
					+ " Z0:256" + " V0:"
					+ (int) Math.ceil(Math.log(vMap.size()) / Math.log(2))
					+ "\n";
			out.write(firstline);
			for (int i = 0; i < actualTupples.size(); i++)
				out.write(actualTupples.get(i) + "\n");
			out.close();

			FileWriter formal = new FileWriter("formal.tuples");
			out = new BufferedWriter(formal);
			firstline = "# M0:"
					+ (int) Math.ceil(Math.log(mMap.size()) / Math.log(2))
					+ " Z:256" + " V0:"
					+ (int) Math.ceil(Math.log(vMap.size()) / Math.log(2))
					+ "\n";
			out.write(firstline);
			for (int i = 0; i < formalTupples.size(); i++)
				out.write(formalTupples.get(i) + "\n");
			out.close();

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("vP0Tupples: " + vP0Tupples.size() + vP0Tupples);
		System.out.println("assignTupples: " + assignTupples.size()
				+ assignTupples);
		System.out.println("vTTupples: " + vTTupples.size() + vTTupples);
		System.out.println("hTTupples: " + hTTupples.size() + hTTupples);
		System.out.println("aTTupples: " + aTTupples.size() + aTTupples);
		System.out.println("chaTupples: " + chaTupples.size() + chaTupples);
		System.out.println("mITupples: " + mITupples.size() + mITupples);
		System.out.println("actualTupples: " + actualTupples.size()
				+ actualTupples);
		System.out.println("formalTupples: " + formalTupples.size()
				+ formalTupples);
		return null;
	}

	public String visit(AssignmentStatement as, String scope) {
		String astupple = null;
		String ret = null;
		String tmp;
		int varidx, rhsidx;
		varidx = rhsidx = -1;
		// System.out.println("scope: " + scope);
		try {
			ret = as.expression.accept(this, scope);
			if (ret != null) {
				tmp = as.identifier.accept(this, scope);
				varidx = this.vMap.indexOf(scope + "." + tmp);
				if (varidx == -1) {
					tmp = scope.substring(0, scope.indexOf('.') + 1) + tmp;
					varidx = this.vMap.indexOf(tmp);
				}
				// System.out.println("varidx: " + varidx);
				if (ret.contains(".new_")) {
					rhsidx = this.hMap.indexOf(ret);
					// if (this.hMapHash.containsKey(ret)) {
					// rhsidx = this.hMap.indexOf(ret, this.hMapHash.get(ret));
					// // this.hMapHash.put(ret, rhsidx + 1);
					// } else {
					// rhsidx = this.hMap.indexOf(ret);
					// // this.hMapHash.put(ret, 1);
					// }
					// this.hMapHash.put(ret, rhsidx + 1);
					astupple = varidx + " " + rhsidx;
					this.vP0Tupples.add(astupple);
					// astupple = vTTupples.get(varidx);
					// astupple = astupple.substring(astupple.indexOf(' ') + 1);
					// astupple = rhsidx + " " + astupple;
					// this.hTTupples.add(astupple);
				} else {
					// System.out.println("ret: " + ret);
					rhsidx = this.vMap.indexOf(scope + "." + ret);
					if (rhsidx != -1) {
						astupple = varidx + " " + rhsidx;
						this.assignTupples.add(astupple);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return astupple;
	}

	public String visit(Expression expr, String scope) {
		return expr.nodeChoice.accept(this, scope);
	}

	public String visit(PrimaryExpression pe, String scope) {
		return pe.nodeChoice.accept(this, scope);
	}

	public String visit(BracketExpression be, String scope) {
		return be.expression.accept(this, scope);
	}

	public String visit(Identifier id, String scope) {
		return id.nodeToken.tokenImage;
	}

	public String visit(AllocationExpression ae, String scope) {
		String alloc = ae.hashCode() + ".new_"
				+ ae.identifier.accept(this, scope);
		String var = scope + "." + ae.hashCode();
		this.vP0Tupples.add(this.vMap.indexOf(var) + " "
				+ this.hMap.indexOf(alloc));
		this.vTTupples.add(this.vMap.indexOf(var) + " "
				+ this.tMap.indexOf(alloc.substring(alloc.indexOf('_') + 1)));
		return alloc;
	}

	public String visit(ArrayAllocationExpression aae, String scope) {
		String aalloc = aae.hashCode() + ".new_" + aae.nodeToken1.tokenImage
				+ aae.nodeToken2.tokenImage + aae.nodeToken3.tokenImage;
		String var = scope + "." + aae.hashCode();
		this.vP0Tupples.add(this.vMap.indexOf(var) + " "
				+ this.hMap.indexOf(aalloc));
		this.vTTupples.add(this.vMap.indexOf(var) + " "
				+ this.tMap.indexOf(aalloc.substring(aalloc.indexOf('_') + 1)));
		return aalloc;
	}

	public String visit(VarDeclaration vd, String scope) {
		String vttupple = null;
		int typeidx, varidx;
		vttupple = vd.type.accept(this, scope);
		typeidx = tMap.indexOf(vd.type.accept(this, scope));
		varidx = vMap.indexOf(scope + "." + vd.identifier.accept(this, scope));
		vttupple = varidx + " " + typeidx;
		vTTupples.add(vttupple);
		return vttupple;
	}

	public String visit(Type t, String scope) {
		return t.nodeChoice.choice.accept(this, scope);
	}

	public String visit(ArrayType at, String scope) {
		return at.nodeToken.tokenImage + at.nodeToken1.tokenImage
				+ at.nodeToken2.tokenImage;
	}

	public String visit(BooleanType bt, String scope) {
		return bt.nodeToken.tokenImage;
	}

	public String visit(IntegerType it, String scope) {
		return it.nodeToken.tokenImage;
	}

	public String visit(MainClass mc, String scope) {
		String type = mc.identifier.accept(this, scope);
		scope = type + "." + mc.nodeToken5.tokenImage;

		mc.identifier1.accept(this, scope);
		mc.printStatement.accept(this, scope);

		return type;
	}

	public String visit(ClassDeclaration cd, String scope) {
		String type = cd.identifier.accept(this, scope);
		scope = type;

		cd.nodeListOptional.accept(this, scope);
		cd.nodeListOptional1.accept(this, scope);

		return null;
	}

	public String visit(ClassExtendsDeclaration ced, String scope) {
		String cedtupple = null;
		String type = ced.identifier.accept(this, scope);
		scope = type;

		int superidx, subidx;
		subidx = tMap.indexOf(ced.identifier.accept(this, scope));
		superidx = tMap.indexOf(ced.identifier1.accept(this, scope));
		cedtupple = superidx + " " + subidx;
		aTTupples.add(cedtupple);

		ced.identifier1.accept(this, scope);
		ced.nodeListOptional.accept(this, scope);
		ced.nodeListOptional1.accept(this, scope);

		return cedtupple;
	}

	public String visit(MethodDeclaration md, String scope) {
		String nname = md.identifier.accept(this, scope);
		String parlist;
		scope += "." + nname;

		md.type.accept(this, scope);
		parlist = md.nodeOptional.accept(this, scope);
		md.nodeListOptional.accept(this, scope);
		md.nodeListOptional1.accept(this, scope);
		md.expression.accept(this, scope);

		if (parlist != null) {
			StringTokenizer tokens = new StringTokenizer(parlist, ",");
			String token;
			int counter = 1;
			int midx = this.mMap.indexOf(scope);
			while (tokens.hasMoreTokens()) {
				token = tokens.nextToken();
				formalTupples.add(midx + " " + counter + " "
						+ this.vMap.indexOf(scope + "." + token));
				counter++;
			}
		}

		return null;
	}

	public String visit(MessageSend ms, String scope) {
		int midx, nidx, iidx;
		String pe, id, invoke;

		pe = ms.primaryExpression.accept(this, scope);
		// if (pe.contains(".new_"))
		// pe = pe.substring(0, pe.indexOf('.'));
		id = ms.identifier.accept(this, scope);
		invoke = pe + "." + id + "@" + ms.hashCode();

		midx = this.mMap.indexOf(scope);
		nidx = this.nMap.indexOf(id);
		iidx = this.iMap.indexOf(invoke);
		// System.out.println(scope + " " + id + " " + invoke);
		this.mITupples.add(midx + " " + iidx + " " + nidx);

		String exprlist = ms.nodeOptional.accept(this, scope);
		StringTokenizer tokens;
		String token, tmp;
		int counter = 1, idx, vidx;

		// if (this.iMapHash.containsKey(invoke)) {
		// idx = this.iMap.indexOf(invoke, this.iMapHash.get(invoke));
		// // this.iMapHash.put(invoke, idx + 1);
		// } else {
		// idx = this.iMap.indexOf(invoke);
		// // this.iMapHash.put(invoke, 1);
		// }
		// this.iMapHash.put(invoke, idx + 1);
		idx = this.iMap.indexOf(invoke);

		if (pe.contains(".new_")) {
			pe = pe.substring(0, pe.indexOf('.'));
		}
		tmp = scope + "." + pe;
		vidx = vMap.indexOf(tmp);
		if (vidx == -1) {
			tmp = scope.substring(0, scope.indexOf('.') + 1) + pe;
			vidx = vMap.indexOf(tmp);
		}
		actualTupples.add(idx + " " + 0 + " " + vidx);

		if (exprlist != null) {
			tokens = new StringTokenizer(exprlist, ",");
			while (tokens.hasMoreTokens()) {
				token = tokens.nextToken();
				if (!token.contains(".new_")) {
					vidx = vMap.indexOf(scope + "." + token);
					if (vidx == -1) {
						tmp = scope + "." + token;
						tmp = tmp.substring(0, tmp.indexOf("."))
								+ tmp.substring(tmp.lastIndexOf("."));
						vidx = vMap.indexOf(tmp);
					}
					actualTupples.add(idx + " " + counter + " " + vidx);
				}
				counter++;
			}
		}

		return invoke;
	}

	public String visit(ExpressionList el, String scope) {
		String exprlist = el.expression.accept(this, scope);
		String expr;
		Node node;
		for (Enumeration<Node> nodes = el.nodeListOptional.elements(); nodes
				.hasMoreElements();) {
			node = nodes.nextElement();
			expr = node.accept(this, scope);
			if (expr != null) {
				exprlist += expr;
			}
		}
		return exprlist;
	}

	public String visit(ExpressionRest er, String scope) {
		return er.nodeToken.tokenImage + er.expression.accept(this, scope);
	}

	public String visit(FormalParameterList fpl, String scope) {
		String fplist = fpl.formalParameter.accept(this, scope);
		String fpar;
		Node node;
		for (Enumeration<Node> nodes = fpl.nodeListOptional.elements(); nodes
				.hasMoreElements();) {
			node = nodes.nextElement();
			fpar = node.accept(this, scope);
			if (fpar != null) {
				fplist += fpar;
			}
		}
		return fplist;
	}

	public String visit(FormalParameterRest er, String scope) {
		return er.nodeToken.tokenImage + er.formalParameter.accept(this, scope);
	}

	public String visit(FormalParameter fp, String scope) {
		String id = fp.identifier.accept(this, scope);
		this.vTTupples.add(this.vMap.indexOf(scope + "." + id) + " " + this.tMap.indexOf(fp.type.accept(this, scope)));
		return id;
	}
}
